/*
 * Created on 08/29/2011 - 09:50 AM
 */
package sembr.vtk.data.input;

import sembr.geometry.interfacing.IDataSet;
import sembr.geometry.shapes.*;
import vtk.*;

/**
 *
 * @author Adriano
 */
public class VtkDataReceiver extends SourceBase
{
    private IDataSet dataSet;
    private vtkPolyDataCollection polyDataCollection;  
    private vtkAppendPolyData appendPolyData;
    private vtkCleanPolyData cleanPolyData;
    private vtkTransformPolyDataFilter tranformFilter;
    private vtkTriangleFilter triangulator;
    
    /**
     * VtkDataReceiver´s Constructor
     * @param IDataSet
     */
    public VtkDataReceiver(IDataSet dataSet)
    {       
        this.dataSet = dataSet;
        polyDataCollection = new vtkPolyDataCollection();
        appendPolyData = new vtkAppendPolyData();
        cleanPolyData = new vtkCleanPolyData();
        tranformFilter = new vtkTransformPolyDataFilter();
        triangulator = new vtkTriangleFilter();
        createShapes();   
        appendPolyData();
        filterDuplicatedPoints();
        scalePolyData();
        //triangulate();
    }
    
    /**
     * @return the polyDataCollection
     */
    public vtkPolyDataCollection getPolyDataCollection() 
    {
        return polyDataCollection;
    }

    /**
     * @param polyDataCollection the polyDataCollection to set
     */
    public void setPolyDataCollection(vtkPolyDataCollection polyDataCollection) 
    {
        this.polyDataCollection = polyDataCollection;
    }   
    
    private void createShapes()
    {
        for (int i = 0; i < this.dataSet.getShapes().size(); i++)
        {
           if (dataSet.getShapes().get(i).getClass() == FaceCollector.class)
           {
               FaceCollector faceCollector = (FaceCollector)dataSet.getShapes().get(i);    
                getPolyDataCollection().AddItem(this.assignFacesToPolyData(faceCollector));               
           }
           
           if (dataSet.getShapes().get(i).getClass() == PolylineCollector.class)
           {
               PolylineCollector polylineCollector = (PolylineCollector)dataSet.getShapes().get(i);
               getPolyDataCollection().AddItem(this.assignPolylinesToPolyData(polylineCollector));  
           }
           
           if (dataSet.getShapes().get(i).getClass() == LWPolylineCollector.class)
           {               
               LWPolylineCollector lwPolylineCollector = (LWPolylineCollector)dataSet.getShapes().get(i);
               vtkPolyDataCollection lwPolylines = this.assignLWPolylinesToPolyData(lwPolylineCollector);
               
               for (int k = 0; k < lwPolylines.GetNumberOfItems(); k++)
               {
                   getPolyDataCollection().AddItem((vtkPolyData)lwPolylines.GetItemAsObject(k));
               }              
           }
           
           if (dataSet.getShapes().get(i).getClass() == PolyfaceCollector.class)
           {
               PolyfaceCollector polyfaceCollector = (PolyfaceCollector)dataSet.getShapes().get(i);
               getPolyDataCollection().AddItem(this.assignPolyfacesToPolydata(polyfaceCollector));
           }
           
           if (dataSet.getShapes().get(i).getClass() == LineCollector.class)
           {
               LineCollector lineCollector = (LineCollector)dataSet.getShapes().get(i);
               getPolyDataCollection().AddItem(this.assignLinesToPolydata(lineCollector));
           }
           
           if (dataSet.getShapes().get(i).getClass() == CircleCollector.class)
           {
               CircleCollector circleCollector = (CircleCollector)dataSet.getShapes().get(i);
               getPolyDataCollection().AddItem(this.assignCirclesToPolydata(circleCollector)); 
           }
           
           if (dataSet.getShapes().get(i).getClass() == ArcsCollector.class)
           {
               // TODO: implement arc generation
           }                 
        }
    }
    
    @Override
    public vtkPolyData getPolyData()
    {
        return tranformFilter.GetOutput();
        //return triangulator.GetOutput();
    }
    
    private vtkPolyData assignFacesToPolyData(FaceCollector faceCollector)
    {
        // instanciate objects required by polydata object
        vtkPolyData faces = new vtkPolyData();
        vtkPoints points = new vtkPoints();
        vtkCellArray cells = new vtkCellArray();    
        vtkDoubleArray colors = new vtkDoubleArray();
        // set array color length by rgb values
        colors.SetNumberOfComponents(3);
                
        // range variable will plus the number of vertices per polyface
        // to set correctly the ids for points
        int range = 0;
        // corners from current acessible face from face´s collection
        double[][] currentCorners;
               
        System.out.println();
        System.out.println("Face collector length: " + faceCollector.length());
        System.out.println();
        
        // stopping criterion
        int nrOfPoints;
        // id for vtkPoints´ points
        int pointId;        
        // set values through the data from faceCollector
        for (int index = 0; index < faceCollector.length(); ++index)
        {
            nrOfPoints = faceCollector.nrOfCorners(index);
            cells.InsertNextCell(nrOfPoints);
            System.out.println("Number of points of Face " + (index + 1) + ": " + nrOfPoints);
            currentCorners = faceCollector.getCornersCoordenatesFromFace(index);
                                  
            for (int i = 0; i < nrOfPoints; ++i)
            {
                pointId = range + i;
                points.InsertPoint(pointId, currentCorners[i]);
                System.out.println("Point ID: " + pointId); 
                cells.InsertCellPoint(pointId);                 
            }

                 //set current color value
                colors.InsertTuple3(index, 
                                    faceCollector.getColor(index)[0], 
                                    faceCollector.getColor(index)[1], 
                                    faceCollector.getColor(index)[2]
                                   );

                range += nrOfPoints;            
        } 
        
        faces.SetPoints(points);
        points.Delete();
        faces.SetPolys(cells);
        cells.Delete();
        faces.GetCellData().SetScalars(colors);
        colors.Delete();
        
        return faces;
    } 
    
    private vtkPolyData assignPolylinesToPolyData(PolylineCollector polylineCollector)
    {
        vtkPolyData polylines = new vtkPolyData();
        vtkPoints points = new vtkPoints();        
        vtkCellArray polylineCells = new vtkCellArray();
        vtkDoubleArray colors = new vtkDoubleArray();
        colors.SetNumberOfComponents(3);
                
        System.out.println();
        System.out.println("Polyline collector length: " + polylineCollector.length());
        System.out.println();
            
        // current vertices for polyline array
        double[][] currentVertices;
        // current polyline from polyline collector
        vtkPolyLine polyline;
        // range variable will plus the number of vertices per polyline
        // to set correctly the ids for points
        int range = 0;
        
        for (int index = 0; index < polylineCollector.length(); index++)
        {            
            int nrOfVertices = polylineCollector.getNrOfVertexAt(index);
                                    
            polyline = new vtkPolyLine();                       
            polyline.GetPointIds().SetNumberOfIds(nrOfVertices);  
            
            System.out.println("Number of Vertex of Polyline " + (index + 1) + ": " + nrOfVertices);
            
            currentVertices = polylineCollector.getVerticesCoordenatesAt(index);
                                    
            for (int i = 0; i < nrOfVertices; i++)
            {              
                int pointId = range + i;
                points.InsertPoint(pointId, currentVertices[i]);
                polyline.GetPointIds().SetId(i, pointId);               
                //scalars.InsertTuple1(pointId, pointId);  
                
                System.out.println("Point ID: " + pointId);   
            }   
            
            //set current color value
            colors.InsertTuple3(index, 
                                polylineCollector.getColor(index)[0], 
                                polylineCollector.getColor(index)[1], 
                                polylineCollector.getColor(index)[2]
                                );
            
            polylineCells.InsertNextCell(polyline);
            // free memory
            polyline.Delete();
            
            range = range + nrOfVertices;
        }       
        
        polylines.SetPoints(points);
        points.Delete();
        polylines.SetLines(polylineCells);
        polylineCells.Delete();
        polylines.GetPointData().SetScalars(colors);
        colors.Delete();
        
        return polylines;                
    }
    
    private vtkPolyDataCollection assignLWPolylinesToPolyData(LWPolylineCollector lwPolylineCollector)
    {
        vtkPolyDataCollection collection = new vtkPolyDataCollection();
        
        for (int i = 0; i < lwPolylineCollector.length(); i++)
        {
            if (lwPolylineCollector.isClosed(i))
               collection.AddItem(assignLWClosedPolylinesToPolydata(lwPolylineCollector));
           else
                collection.AddItem(assignLWOpenedPolylinesToPolyData(lwPolylineCollector));
        }
        
        return collection;
    }
    
    private vtkPolyData assignLWOpenedPolylinesToPolyData(LWPolylineCollector polylineCollector)
    {
        vtkPolyData polylines = new vtkPolyData();
        vtkPoints points = new vtkPoints();        
        vtkCellArray polylineCells = new vtkCellArray();
        vtkDoubleArray colors = new vtkDoubleArray();
        colors.SetNumberOfComponents(3);
                
        System.out.println();
        System.out.println("Polyline collector length: " + polylineCollector.length());
        System.out.println();
            
        // current vertices for polyline array
        double[][] currentVertices;
        // current polyline from polyline collector
        vtkPolyLine polyline;
        // range variable will plus the number of vertices per polyline
        // to set correctly the ids for points
        int range = 0;
        
        for (int index = 0; index < polylineCollector.length(); index++)
        {            
            int nrOfVertices = polylineCollector.getNumberOfVerticesAt(index);
                                    
            polyline = new vtkPolyLine();                       
            polyline.GetPointIds().SetNumberOfIds(nrOfVertices);  
            
            System.out.println("Number of Vertex of Polyline " + (index + 1) + ": " + nrOfVertices);
            
            currentVertices = polylineCollector.getVerticesCoordenatesAt(index);
                                    
            for (int i = 0; i < nrOfVertices; i++)
            {              
                int pointId = range + i;
                points.InsertPoint(pointId, currentVertices[i]);
                polyline.GetPointIds().SetId(i, pointId);               
                //scalars.InsertTuple1(pointId, pointId);  
                
                System.out.println("Point ID: " + pointId);   
            }   
            
            //set current color value
            colors.InsertTuple3(index, 
                                polylineCollector.getColor(index)[0], 
                                polylineCollector.getColor(index)[1], 
                                polylineCollector.getColor(index)[2]
                                );
            
            polylineCells.InsertNextCell(polyline);
            // free memory
            polyline.Delete();
            
            range = range + nrOfVertices;
        }       
        
        polylines.SetPoints(points);
        points.Delete();
        polylines.SetLines(polylineCells);
        polylineCells.Delete();
        polylines.GetPointData().SetScalars(colors);
        colors.Delete();
        
        return polylines;                
    }
    
    private vtkPolyData assignLWClosedPolylinesToPolydata(LWPolylineCollector polyfaceCollector)
    {
        vtkPolyData polyfaces = new vtkPolyData();
        vtkPoints points = new vtkPoints();
        vtkCellArray polyfaceCells = new vtkCellArray();
        vtkDoubleArray colors = new vtkDoubleArray();
        colors.SetNumberOfComponents(3);
        // range variable will plus the number of vertices per polyface
        // to set correctly the ids for points
        int range = 0;
        
        System.out.println();
        System.out.println("Polyface collector length: " + polyfaceCollector.length());
        System.out.println();
        
        // number of vertices of current face
        int nrOfVertices;
        // number of faces in polyface collector
        int nrOfFaces;
        // current vertices for current face in polyface collector
        double[][] currentVertices;
               
        for (int index = 0; index < polyfaceCollector.length(); index++)
        {
            nrOfVertices = polyfaceCollector.getNumberOfVerticesAt(index);
            System.out.println("Number of Vertices of Polyface " + (index + 1) + ": " + nrOfVertices);
            
            nrOfFaces = polyfaceCollector.length();
            System.out.println("Number of Faces of Polyface " + (index + 1) + ": " + nrOfFaces);
            
            currentVertices = polyfaceCollector.getVerticesCoordenatesAt(index);            
            polyfaceCells.InsertNextCell(nrOfVertices);
            
            int pointId;
            
            for (int i = 0; i < nrOfVertices; i++)
            {
                pointId = range + i;
                points.InsertPoint(pointId, currentVertices[i]);
                polyfaceCells.InsertCellPoint(pointId);
                System.out.println("Point ID: " + pointId);                
            }
            
            int color[] = polyfaceCollector.getColor(index);
            
            //set current color value
            colors.InsertTuple3(index, 
                                polyfaceCollector.getColor(index)[0], 
                                polyfaceCollector.getColor(index)[1], 
                                polyfaceCollector.getColor(index)[2]
                                );
                
            // update range value
            range = range + nrOfVertices;
        }
        
        polyfaces.SetPoints(points);
        points.Delete();
        polyfaces.SetPolys(polyfaceCells);
        polyfaceCells.Delete();
        polyfaces.GetCellData().SetScalars(colors);
        colors.Delete();
        
//        // TODO: não esquecer de apagar isso
//        VtkXmlDataSetWriter testWriter = new VtkXmlDataSetWriter("testeChisEMiEl");
//        testWriter.setInputByPolydata(polyfaces);
//        testWriter.writeFile();
        
        return polyfaces;
    }
    
    private vtkPolyData assignPolyfacesToPolydata(PolyfaceCollector polyfaceCollector)
    {
        vtkPolyData polyfaces = new vtkPolyData();
        vtkPoints points = new vtkPoints();
        vtkCellArray polyfaceCells = new vtkCellArray();
        vtkDoubleArray colors = new vtkDoubleArray();
        colors.SetNumberOfComponents(3);
        // range variable will plus the number of vertices per polyface
        // to set correctly the ids for points
        int range = 0;
        
        System.out.println();
        System.out.println("Polyface collector length: " + polyfaceCollector.length());
        System.out.println();
                
        // number of vertices of current face
        int nrOfVertices;
        // number of faces in polyface collector
        int nrOfFaces;
        // current vertices for current face in polyface collector
        double[][] currentVertices;
        // current ids for current vertices
        int[][] currentIds;
        int collectorLength = polyfaceCollector.length();
        
        for (int index = 0; index < collectorLength; index++)
        {
            nrOfVertices = polyfaceCollector.getNrOfVertexAt(index);
            System.out.println("Number of Vertices of Polyface " + (index + 1) + ": " + nrOfVertices);
            
            nrOfFaces = polyfaceCollector.getNrOfFacesAt(index);
            System.out.println("Number of Faces of Polyface " + (index + 1) + ": " + nrOfFaces);
            
            currentVertices = polyfaceCollector.getVerticesCoordenatesAt(index);            
            currentIds = polyfaceCollector.getFacesIndexesAt(index);
                        
            int pointId;
            
            for (int i = 1; i <= nrOfVertices; i++)
            {
                pointId = range + i;
                // currentVertices´s index is desllocated by 1 at left 
                // because indexes from polyfaces begin at 1 (i = 1)
                points.InsertPoint(pointId, currentVertices[i - 1]);
                
                System.out.println("Point ID: " + pointId);                
            }
            
            for(int k = 0; k < nrOfFaces; k++)
            {   
                // polyfaces contain faces with four vertices each
                polyfaceCells.InsertNextCell(4);
                polyfaceCells.InsertCellPoint(currentIds[k][0] + range);
                polyfaceCells.InsertCellPoint(currentIds[k][1] + range);
                polyfaceCells.InsertCellPoint(currentIds[k][2] + range);
                polyfaceCells.InsertCellPoint(currentIds[k][3] + range);
            }  
            
            int[] currentColor = polyfaceCollector.getColor(index);
            
            //set current color value
            colors.InsertTuple3(index, 
                                currentColor[0], 
                                currentColor[1], 
                                currentColor[2]
                                );
                
            // update range value
            range = range + nrOfVertices;
        }
        
        polyfaces.SetPoints(points);
        points.Delete();
        polyfaces.SetPolys(polyfaceCells);
        polyfaceCells.Delete();
        polyfaces.GetCellData().SetScalars(colors);
        colors.Delete();
        
        return polyfaces;
    }
    
    private vtkPolyData assignLinesToPolydata(LineCollector lineCollector)
    {
        vtkPolyData lines = new vtkPolyData();
        vtkPoints points = new vtkPoints();
        vtkCellArray lineCells = new vtkCellArray();
        vtkDoubleArray colors = new vtkDoubleArray();
        colors.SetNumberOfComponents(3);
        
        // poinId is set to -1 and will be increment by 1 at first loop
        int pointId = -1;
        
        System.out.println();
        System.out.println("Number of lines: " + lineCollector.length());
                
        for (int index = 0; index < lineCollector.length(); index++)
        {
            System.out.println();            
            pointId++;
            System.out.println("Point ID: " + pointId);
            points.InsertPoint(pointId, lineCollector.getStartPointAt(index));
            
            // new value to pointId
            // end point Id
            pointId++; 
            System.out.println("Point ID: " + pointId);
            points.InsertPoint(pointId, lineCollector.getEndPointAt(index));          
        }
        
        pointId = -1;
        
        for (int index = 0; index < lineCollector.length(); index++)
        {
            System.out.println();
            pointId++;
            // each line have two points: a start and end points
            vtkLine currentLine = new vtkLine();
            System.out.println("Line: " + (index + 1));           
            System.out.println("Start point ID: " + pointId);
            currentLine.GetPointIds().SetId(0, pointId);
            // new value to pointId
            // end point Id
            pointId++;            
            System.out.println("End Point ID: " + pointId);           
            // end point for the current line is
            // the start point for the next line
            // index parameter increment here is 
            // the same value for the next index 
            // incremented in the loop            
            // TODO: verificar implementação de indices da linha
            currentLine.GetPointIds().SetId(1, pointId);     
            
            lineCells.InsertNextCell(currentLine);
            colors.InsertTuple3(index, 
                                lineCollector.getColor(index)[0],
                                lineCollector.getColor(index)[1],
                                lineCollector.getColor(index)[2]);
            // free memory
            //currentLine.Delete();
        }
        
        lines.SetPoints(points);
        points.Delete();
        lines.SetLines(lineCells);
        lineCells.Delete();
        lines.GetCellData().SetScalars(colors);
        
        return lines;
    }
      
    private vtkPolyData assignCirclesToPolydata(CircleCollector circleCollector)
    {       
        vtkPolyData polyData;
        vtkDoubleArray colors = new vtkDoubleArray();                
        vtkAppendPolyData appender = new vtkAppendPolyData();
        vtkRegularPolygonSource circle;
        
        for (int i = 0; i < circleCollector.length(); i++)
        {
            circle = new vtkRegularPolygonSource();
            circle.SetCenter(circleCollector.getCenter(i));
            circle.SetRadius(circleCollector.getRadius(i));
            // fixed number of sides
            // TODO: verify the corret number os sides, maybe it can be a parameter
            circle.SetNumberOfSides(200);
            circle.Update();
            colors.InsertNextTuple1(230.00);
            polyData = circle.GetOutput();
            polyData.GetCellData().SetScalars(colors);
            appender.AddInput(polyData);
        }
         
        return appender.GetOutput();
    }
   
    private vtkPolyData assignVertexToPolydata(PolylineCollector polylineCollector)
    {
        vtkPolyData vertices = new vtkPolyData();
        vtkPoints points = new vtkPoints();        
        vtkCellArray polyVertexCells = new vtkCellArray();
        //vtkDoubleArray scalars = new vtkDoubleArray();                               
                   
        int index;   
                                
        for (index = 0; index < polylineCollector.length(); index++)
        { 
            vtkPolyVertex polyVertex = new vtkPolyVertex();           
            
            int nrOfVertices = polylineCollector.getNrOfVertexAt(index);
            
            polyVertex.GetPointIds().SetNumberOfIds(nrOfVertices);
                    
            for (int i = 0; i < nrOfVertices; i++)
            {              
                int pointId = index * nrOfVertices + i;
                points.InsertPoint(pointId, polylineCollector.getVerticesCoordenatesAt(index)[i]);
                polyVertex.GetPointIds().SetId(i, pointId);
                // scalars.InsertTuple1(pointId, pointId);              
            }     
            
            // add current cell to cell´s collection 
            polyVertexCells.InsertNextCell(polyVertex);
            // free memory
            polyVertex.Delete();
        }       
         
        vertices.SetPoints(points);
        points.Delete();
        vertices.SetVerts(polyVertexCells);
        polyVertexCells.Delete();
        //polylines.GetPointData().SetScalars(scalars);
        //scalars.Delete();
        
        return vertices;
    }
    
    private void appendPolyData()
    {
        // receives polydatas from collection                       
        for(int i = 0; i < getPolyDataCollection().GetNumberOfItems(); i++)            
        {
            vtkPolyData currentPolyData = (vtkPolyData)getPolyDataCollection().GetItemAsObject(i);                                    
            appendPolyData.SetInputConnection(currentPolyData.GetProducerPort());
        }
        
        appendPolyData.Update();        
    }
    
    private void filterDuplicatedPoints()
    {
        cleanPolyData.SetInputConnection(this.appendPolyData.GetOutputPort());
        cleanPolyData.Update();        
    }
    
    private void scalePolyData()
    {
        vtkTransform transformer = new vtkTransform();
        transformer.PostMultiply();
        transformer.Scale(1, 1, 1);
        //transformer.Translate(-51, -5, -10);
        transformer.Update();
                
        tranformFilter.SetInput(cleanPolyData.GetOutput());
        tranformFilter.SetTransform(transformer);    
        tranformFilter.Update();
    }
    
    public void translateXToRight(double range)
    {
//        vtkTransform transformer = new vtkTransform();
//        //transformer.PostMultiply();
//        transformer.Translate(range, 0.00, 0.00);
//        transformer.Update();
//        
//        tranformFilter.SetInput(getPolyData());
//        tranformFilter.SetTransform(transformer);    
//        tranformFilter.Update();
    }
       
    // TODO: this is a testing method for verifing triangularization
    private void triangulate()
    {
        //delaunay3D = new vtkDelaunay3D();
        triangulator.SetInput(tranformFilter.GetOutput());
        triangulator.Update();
    } 
}
